﻿Imports System.ComponentModel
Imports Mysoft.Map.Data
Imports Mysoft.Map.Application.Controls
Imports System.Runtime.CompilerServices
Imports System.Xml
Imports Mysoft.Map.Caching
Imports System.Reflection
Imports System.Security.Permissions
Imports System.IO
Imports System.Text
Imports System.Web.UI
Imports System.Collections.Generic

Namespace Control
    ''' <summary>
    ''' 导航树控件
    ''' </summary>
    <DefaultProperty("Guid"), ToolboxData("<{0}:NavTree runat=""server""></{0}:NavTree>")> _
    Public Class NavTree
        Inherits System.Web.UI.Control
        Private _treeGuid As Guid = Guid.Empty
        Private _siteGuid As String = String.Empty
        Private _name As String = String.Empty
        Private _type As NavTreeType = NavTreeType.Normal
        Private _userDefinedUrl As String
        Private _rootNode As NavTreeNode
        Private _defaultExpandLevel As Integer = 0
        Private _isEditMode As Boolean = False
        Private _autoInit As Boolean = True
        Private _animated As Boolean = False
        Private _IconEnabled As Boolean = True
        Private _defaultDataSql As String = String.Empty

        ''' <summary>
        ''' 导航树的GUID
        ''' </summary>
        <Bindable(True), Description("导航树的Guid"), DefaultValue(False), Category("Data")> _
        Public Property TreeGuid() As Guid
            Get
                If _treeGuid = Guid.Empty Then
                    _treeGuid = Guid.NewGuid
                End If
                Return _treeGuid
            End Get
            Set(ByVal value As Guid)
                _treeGuid = value
                Dim sql As StringBuilder = New StringBuilder()
                sql.AppendFormat(Me.datasql, value.ToString())
                Dim data As DataTable = MyDB.GetDataTable(sql.ToString())
                If data.Rows.Count > 0 Then
                    _name = data.Rows(0)("NavTreeName").ToString()
                    Select Case CType(data.Rows(0)("NavTreeMode"), Integer)
                        Case 0
                            _type = NavTreeType.Normal
                            Dim xmldoc As XmlDocument = New XmlDocument()
                            xmldoc.LoadXml(data.Rows(0)("NavTreeXML").ToString())
                            _rootNode = New NavTreeNode()
                            _rootNode.Xml = xmldoc.DocumentElement
                            Exit Select
                        Case 1
                            _type = NavTreeType.UserDefined
                            _siteGuid = data.Rows(0)("SiteGUID").ToString()
                            _userDefinedUrl = data.Rows(0)("URL").ToString()
                            Exit Select
                    End Select
                End If
            End Set
        End Property
        ''' <summary>
        ''' 根节点
        ''' </summary>
        <Bindable(False), Description("导航树的根节点"), DefaultValue(False), Category("Data")> _
        Public ReadOnly Property RootNode() As NavTreeNode
            Get
                Return _rootNode
            End Get
        End Property

        ''' <summary>
        ''' 默认数据源sql
        ''' </summary>
        <Bindable(True), Description("默认是否使用图标"), DefaultValue(""), Category("Data")> _
      Public Property datasql() As String
            Get
                If _defaultDataSql Is String.Empty Then
                    Return "SELECT [NavTreeName],[SiteGUID], [NavTreeXML],[URL],[NavTreeMode] FROM [km_NavTree] WHERE  [NavTreeGuid]='{0}'"
                Else
                    Return _defaultDataSql
                End If
            End Get
            Set(ByVal value As String)
                _defaultDataSql = value
            End Set
        End Property
        ''' <summary>
        ''' 默认是否使用图标
        ''' </summary>
        <Bindable(True), Description("默认是否使用图标"), DefaultValue(True), Category("Appearance")> _
      Public Property EnableIcon() As Boolean
            Get
                Return _IconEnabled
            End Get
            Set(ByVal value As Boolean)
                _IconEnabled = value
            End Set
        End Property

        ''' <summary>
        ''' 默认展开层数
        ''' </summary>
        <Bindable(True), Description("默认展开层数"), DefaultValue(False), Category("Appearance")> _
        Public Property DefaultExpandLevel() As Integer
            Get
                Return _defaultExpandLevel
            End Get
            Set(ByVal value As Integer)
                If value > 0 Then
                    _defaultExpandLevel = value
                End If
            End Set
        End Property
        ''' <summary>
        ''' 是否是编辑模式
        ''' </summary>
        <Bindable(True), Description("是否是编辑模式"), DefaultValue(False), Category("Appearance")> _
        Public Property IsEditMode() As Boolean
            Get
                Return _isEditMode
            End Get
            Set(ByVal value As Boolean)
                _isEditMode = value
            End Set
        End Property
        ''' <summary>
        ''' 是否在页面加载后自动初始化
        ''' </summary>
        <Bindable(True), Description("是否在页面加载后自动初始化"), DefaultValue(True), Category("Appearance")> _
        Public Property AutoInit() As Boolean
            Get
                Return _autoInit
            End Get
            Set(ByVal value As Boolean)
                _autoInit = value
            End Set
        End Property
        <Bindable(True), Description("是否动画效果展示子节点"), DefaultValue(False), Category("Appearance")> _
        Public Property Animated() As Boolean
            Get
                Return _animated
            End Get
            Set(ByVal value As Boolean)
                _animated = value
            End Set
        End Property
        ''' <summary>
        ''' 自定义XML
        ''' </summary>
        <Bindable(True), Description("自定义XML"), DefaultValue(""), Category("Data")> _
        Public Property Xml() As String
            Get
                If _rootNode IsNot Nothing Then
                    Return _rootNode.Xml.OuterXml
                End If
                Return Nothing
            End Get
            Set(ByVal value As String)
                Dim xmldoc As XmlDocument = New XmlDocument()
                xmldoc.LoadXml(value)
                _rootNode = New NavTreeNode()
                _rootNode.Xml = xmldoc.DocumentElement
            End Set
        End Property
        Protected Overrides Sub Render(ByVal output As HtmlTextWriter)
            Select Case _type
                Case NavTreeType.Normal '输出普通类型
                    If RootNode IsNot Nothing Then
                        output.WriteLine("<ul id=""" + Me.ClientID + """ class=""filetree"" enableIcon=""" & Me.EnableIcon.ToString.ToLower & """>")

                        RootNode.Render(output, Me)
                        output.WriteLine("</ul>")

                        output.WriteLine("<script type='text/javascript'>")
                        output.WriteLine("var " + Me.ClientID + "=null;")
                        If _autoInit Then
                            'output.WriteLine("$(function(){" + Me.ClientID + "=$('#" + Me.ClientID + "').treeview();})")
                            '在加载时会出现延迟
                            output.Write(Me.ClientID + "=$('#" + Me.ClientID + "').treeview(")
                            If _animated Then
                                output.Write("{animated:true}")
                            End If
                            output.WriteLine(");")
                        End If

                        output.WriteLine("</script>")

                    End If
                    Exit Select
                Case NavTreeType.UserDefined '输出自定义类型
                    If _userDefinedUrl IsNot Nothing Then
                        If Not Me.IsEditMode Then
                            ' output.WriteLine("<iframe style=""width:100%;height:100%"" frameborder=""0"" src=""" + Mysoft.EKP.Business.AddressManager.GetUrl(_siteGuid, _userDefinedUrl) + """ ></iframe>")
                            output.WriteLine("<iframe style=""width:100%;height:100%"" frameborder=""0""></iframe>")
                        End If
                    End If
            End Select

        End Sub
        ''' <summary>
        ''' 获取展示HTML代码
        ''' </summary>
        ''' <returns>此导航树的展示HTML代码</returns>
        ''' <remarks></remarks>
        Public Function GetHTML() As String
            Dim writer As StringWriter = New StringWriter()
            Dim output As HtmlTextWriter = New HtmlTextWriter(writer)
            Me.Render(output)
            Return writer.ToString()
        End Function
    End Class
    ''' <summary>
    ''' 导航树类型
    ''' </summary>
    ''' <remarks></remarks>
    Public Enum NavTreeType As Integer
        Normal = 0
        UserDefined = 1
    End Enum
    ''' <summary>
    ''' 导航树节点类型
    ''' </summary>
    ''' <remarks></remarks>
    Public Enum NavTreeNodeType As Integer
        Url = 0
        Reflection = 1
    End Enum
    ''' <summary>
    ''' 导航树节点类
    ''' </summary>
    ''' <remarks></remarks>
    Public Class NavTreeNode
        Public Shared ReadOnly ATTRIBUTE_NAME_COLLECTION As System.String() = New String() {"text", "navType", "assembly", "invokeClass", "invokeFunction", "siteGUID", "url", "target", "icon", "asyn"}
        Private _type As NavTreeNodeType = NavTreeNodeType.Url
        Private _childNodes As List(Of NavTreeNode)
        Private _parentNode As NavTreeNode
        Private _nodexml As XmlElement
        Private _text As String = String.Empty
        Private _url As String = String.Empty
        Private _target As String = String.Empty
        Private _assembly As String = String.Empty
        Private _class As String = String.Empty
        Private _function As String = String.Empty
        Private _level As Integer = 0
        Private _icon As String = String.Empty
        Private _isAsyn As Boolean = False
        Private _isExpanded As Boolean = False
        Private _params As Object()

        ''' <summary>
        ''' 子节点列表
        ''' </summary>
        Public ReadOnly Property ChildNodes() As List(Of NavTreeNode)
            Get
                If _childNodes Is Nothing Then
                    _childNodes = New List(Of NavTreeNode)
                End If
                Return _childNodes
            End Get
        End Property
        ''' <summary>
        ''' 父节点
        ''' </summary>
        Public Property ParentNode() As NavTreeNode
            Get
                Return _parentNode
            End Get
            Set(ByVal value As NavTreeNode)
                _parentNode = value
            End Set
        End Property
        ''' <summary>
        ''' 显示文本
        ''' </summary>
        Public Property Text() As String
            Get
                Return _text
            End Get
            Set(ByVal value As String)
                _text = value
            End Set
        End Property
        ''' <summary>
        ''' 链接地址
        ''' </summary>
        Public Property Url() As String
            Get
                Return _url
            End Get
            Set(ByVal value As String)
                _url = value
            End Set
        End Property
        ''' <summary>
        ''' 链接目标
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Property Target() As String
            Get
                Return _target
            End Get
            Set(ByVal value As String)
                _target = value
            End Set
        End Property
        ''' <summary>
        ''' 通过访问函数获取动态节点的程序集
        ''' </summary>
        Public Property Assembly() As String
            Get
                Return _assembly
            End Get
            Set(ByVal value As String)
                _assembly = value
            End Set
        End Property
        ''' <summary>
        ''' 通过访问函数获取动态节点的类
        ''' </summary>
        Public Property InvokeClass() As String
            Get
                Return _class
            End Get
            Set(ByVal value As String)
                _class = value
            End Set
        End Property
        ''' <summary>
        ''' 通过访问函数获取动态节点的函数
        ''' </summary>
        Public Property InvokeFunction() As String
            Get
                Return _function
            End Get
            Set(ByVal value As String)
                _function = value
            End Set
        End Property
        ''' <summary>
        ''' 用户定义的图标
        ''' </summary>
        Public Property Icon() As String
            Get
                Return _icon
            End Get
            Set(ByVal value As String)
                _icon = value
            End Set
        End Property
        ''' <summary>
        ''' 是否叶子节点（不包含子节点）
        ''' </summary>
        Public Property IsLeaf() As Boolean
            Get
                If _childNodes Is Nothing Then
                    Return True
                Else
                    If _childNodes.Count = 0 Then
                        Return True
                    End If
                End If
                Return False
            End Get
            Set(ByVal value As Boolean)
                If value And _childNodes IsNot Nothing Then
                    _childNodes.Clear()
                End If
            End Set
        End Property
        ''' <summary>
        ''' 级数
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Property Level() As Integer
            Get
                Return _level
            End Get
            Set(ByVal value As Integer)
                _level = value
            End Set
        End Property
        ''' <summary>
        ''' 是否异步加载
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Property IsAsyn() As Boolean
            Get
                Return _isAsyn
            End Get
            Set(ByVal value As Boolean)
                _isAsyn = value
            End Set
        End Property
        ''' <summary>
        ''' 是否默认展开
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Property IsExpanded() As Boolean
            Get
                Return _isExpanded
            End Get
            Set(ByVal value As Boolean)
                _isExpanded = value
            End Set
        End Property
        ''' <summary>
        ''' 通过访问函数获取动态节点的函数参数
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Property Params() As Object()
            Get
                Return _params
            End Get
            Set(ByVal value As Object())
                _params = value
            End Set
        End Property

        ''' <summary>
        ''' 表示此节点及其子节点的XML
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks>设置此属性将重建此节点及其子节点</remarks>
        Public Property Xml() As XmlElement
            Get
                Return _nodexml
            End Get
            Set(ByVal value As XmlElement)
                _nodexml = value
                Try
                    _text = value.Attributes("text").Value '显示文本

                    '导航类型
                    If value.Attributes("navType") Is Nothing Then
                        _type = NavTreeNodeType.Url
                    Else
                        If value.Attributes("navType").Value = "1" Then
                            _type = NavTreeNodeType.Reflection
                        Else
                            _type = NavTreeNodeType.Url
                        End If
                    End If
                    '指定图标
                    If value.Attributes("icon") IsNot Nothing Then
                        _icon = value.Attributes("icon").Value
                    End If
                    '打开方式
                    If value.Attributes("target") IsNot Nothing Then
                        _target = value.Attributes("target").Value
                    End If
                    '是否异步加载
                    If value.Attributes("asyn") IsNot Nothing Then
                        _isAsyn = Boolean.Parse(value.Attributes("asyn").Value)
                    End If
                    '是否默认展开
                    If value.Attributes("expanded") IsNot Nothing Then
                        _isExpanded = Boolean.Parse(value.Attributes("expanded").Value)
                        If Me.ParentNode IsNot Nothing Then
                            '如果父级不展开则自己也不展开
                            _isExpanded = _isExpanded AndAlso Me.ParentNode.IsExpanded
                        End If
                    End If


                    '特殊节点或者异步加载节点需要读取程序集、类、函数、参数
                    If _type = NavTreeNodeType.Reflection Or _isAsyn Then
                        _assembly = value.Attributes("assembly").Value
                        _class = value.Attributes("invokeClass").Value
                        _function = value.Attributes("invokeFunction").Value
                        '参数
                        Dim params = value.SelectNodes("param")
                        If params.Count > 0 Then
                            ReDim _params(params.Count - 1)
                            Dim i = 0
                            For Each child As XmlNode In params
								_params(i) = child.InnerText
								i += 1
                            Next
                        End If
                    End If

                    '普通节点,计算地址
                    If _type = NavTreeNodeType.Url Then
                        Dim address = ""
                        If value.Attributes("url") IsNot Nothing Then
                            If value.Attributes("siteGUID") Is Nothing Then
                                _url = value.Attributes("url").Value
                            Else
                                '_url = Mysoft.EKP.Business.AddressManager.GetUrl(value.Attributes("siteGUID").Value, value.Attributes("url").Value)
                            End If
                        End If
                    End If

                    Me._childNodes = New List(Of NavTreeNode)
                    For Each child As XmlNode In value.SelectNodes("item")
                        Dim node = New NavTreeNode()
                        Me.AppandChild(node)
                        node.Xml = child
                    Next
                Catch ex As Exception
                    _text = "解析XML错误"
                    _type = NavTreeNodeType.Url
                End Try
            End Set
        End Property
        ''' <summary>
        ''' 添加子节点
        ''' </summary>
        ''' <param name="child"></param>
        ''' <remarks></remarks>
        Public Sub AppandChild(ByVal child As NavTreeNode)
            child.ParentNode = Me
            If _childNodes Is Nothing Then
                _childNodes = New List(Of NavTreeNode)
            End If
            Me._childNodes.Add(child)
            child.Level = Me.Level + 1
        End Sub
        Protected Friend Sub Render(ByVal output As HtmlTextWriter, ByVal panel As NavTree)
            If Not panel.IsEditMode And _type = NavTreeNodeType.Reflection And Not IsAsyn Then
                '非编辑模式下，如果是特殊节点且不采用异步加载时，通过反射读取XML替换此节点输出
                Try
                    '通过反射获取子节点
                    Dim data = Mysoft.Map.Utility.General.Invoke(_assembly, _class, _function, _params).ToString()
                    Dim newnodexml = New XmlDocument()
                    Try
                        newnodexml.LoadXml(data)
                    Catch ex As Exception
                        newnodexml.LoadXml("<item text=""" + _text + """>" + data + "</item>")
                    End Try
                    '根据获取的xml生成新节点替换此节点
                    Dim newnode = New NavTreeNode()
                    newnode.Level = Me.Level
                    newnode.ParentNode = Me.ParentNode
                    newnode.Xml = newnodexml.DocumentElement
                    '输出新节点替换此节点输出
                    newnode.Render(output, panel)
                Catch ex As Exception
                    If panel.EnableIcon Then
                        output.Write("<li><span class=""file"">" + Me.Text + "(解析失败)</span></li>")
                    Else
                        output.Write("<li><span>" + Me.Text + "(解析失败)</span></li>")
                    End If
                End Try
            Else
                '==========================================输出li
                output.Write("<li ")
                '级数超过默认展开级数的且没有标记为展开的节点收缩(异步加载节点默认为收缩)
                If _isAsyn Or (Level > panel.DefaultExpandLevel And Not _isExpanded) Then
                    output.Write("class=""closed"" ")
                End If
            '输出属性
            For Each attr As XmlAttribute In _nodexml.Attributes
                '编辑模式或者异步加载输出所有属性；否则只输出用户定义的属性
                If panel.IsEditMode Or _isAsyn Or (Not ATTRIBUTE_NAME_COLLECTION.Contains(attr.Name)) Then
                    If attr.Name = "navType" And attr.Value <> "1" Then
                        Continue For
                    End If
                    output.Write(attr.Name + "=""" + attr.Value + """ ")
                End If
            Next
            '标记需要异步加载
            If _isAsyn Then
                output.Write(" isNeedAsynLoad=""true"" ")
            End If
            output.WriteLine(" >")
            '==========================================输出文本部分
            '输出头(span或者a)
            Dim isSpan As Boolean = False
            If (Not panel.IsEditMode) And (_type = NavTreeNodeType.Url AndAlso _url IsNot Nothing AndAlso _url.Length > 0) Then
                '非编辑模式下；普通节点类型并且指定了url输出为超链接
                If Me.Target = "_self" Or Me.Target = "" Then
                    '自身窗体打开
                    output.Write("<a href=""javascript:openInTop('" + Me.Url + "')""  ")
                Else
                    '指定位置打开
                    output.Write("<a href='" + Me.Url + "' target='" + Me.Target + "' ")
                End If
            Else
                '编辑模式下、或者特殊节点、或者没有指定url，输出为span
                output.Write("<span ")
                isSpan = True
            End If
            '是否末级节点
            If _isAsyn Then
                '异步加载的节点统一为文件夹节点
                If panel.EnableIcon Then
                    output.Write(" class=""folder"" ")
                End If
            Else
                If panel.EnableIcon Then
                    output.Write(" class=""" + IIf(IsLeaf, "file", "folder") + """ ")
                End If
            End If
            If panel.EnableIcon Then
                '指定图标
                If Me.Icon.Length > 0 Then
                    output.Write(" style=""background-image: url(/PubProject/images/" + Me.Icon + ");background-position: 0 0;background-repeat:no-repeat"" ")
                End If
            End If
            '输出显示文本
            output.Write(">" + _text)

            '输出span或者a结尾
            If isSpan Then
                output.WriteLine("</span>")
            Else
                output.WriteLine("</a>")
            End If
            '==========================================输出参数为hidden input
            If _params IsNot Nothing AndAlso _params.Length > 0 Then
                For Each param As Object In _params
                    If param IsNot Nothing Then
                        output.WriteLine("<input type=""hidden"" value=""" + param.ToString() + """/>")
                    End If
                Next
            End If
            '==========================================输出子节点
            If Not IsLeaf Or _isAsyn Then
                output.WriteLine("<ul>")
                For Each child As NavTreeNode In ChildNodes
                    child.Render(output, panel)
                Next
                output.WriteLine("</ul>")
            End If
            output.WriteLine("</li>")
                End If
        End Sub
        ''' <summary>
        ''' 获取展示HTML代码
        ''' </summary>
        ''' <returns>此节点的展示HTML代码</returns>
        ''' <remarks></remarks>
        Public Function GetHTML() As String
            Dim writer As StringWriter = New StringWriter()
            Dim output As HtmlTextWriter = New HtmlTextWriter(writer)
            Dim panel As NavTree = New NavTree
            Me.Render(output, panel)
            Return writer.ToString()
        End Function
    End Class
End Namespace

